package com.fzy.leetcode.editor.cn;
//2023-03-26 21:30:37
//请你为 最不经常使用（LFU）缓存算法设计并实现数据结构。 
//
// 实现 LFUCache 类： 
//
// 
// LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象 
// int get(int key) - 如果键 key 存在于缓存中，则获取键的值，否则返回 -1 。 
// void put(int key, int value) - 如果键 key 已存在，则变更其值；如果键不存在，请插入键值对。当缓存达到其容量 
//capacity 时，则应该在插入新项之前，移除最不经常使用的项。在此问题中，当存在平局（即两个或更多个键具有相同使用频率）时，应该去除 最近最久未使用 的键。 
// 
//
// 为了确定最不常使用的键，可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。 
//
// 当一个键首次插入到缓存中时，它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作，使用计数器的值将会递增。 
//
// 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。 
//
// 
//
// 示例： 
//
// 
//输入：
//["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", 
//"get"]
//[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
//输出：
//[null, null, null, 1, null, -1, 3, null, -1, 3, 4]
//
//解释：
//// cnt(x) = 键 x 的使用计数
//// cache=[] 将显示最后一次使用的顺序（最左边的元素是最近的）
//LFUCache lfu = new LFUCache(2);
//lfu.put(1, 1);   // cache=[1,_], cnt(1)=1
//lfu.put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
//lfu.get(1);      // 返回 1
//                 // cache=[1,2], cnt(2)=1, cnt(1)=2
//lfu.put(3, 3);   // 去除键 2 ，因为 cnt(2)=1 ，使用计数最小
//                 // cache=[3,1], cnt(3)=1, cnt(1)=2
//lfu.get(2);      // 返回 -1（未找到）
//lfu.get(3);      // 返回 3
//                 // cache=[3,1], cnt(3)=2, cnt(1)=2
//lfu.put(4, 4);   // 去除键 1 ，1 和 3 的 cnt 相同，但 1 最久未使用
//                 // cache=[4,3], cnt(4)=1, cnt(3)=2
//lfu.get(1);      // 返回 -1（未找到）
//lfu.get(3);      // 返回 3
//                 // cache=[3,4], cnt(4)=1, cnt(3)=3
//lfu.get(4);      // 返回 4
//                 // cache=[3,4], cnt(4)=2, cnt(3)=3 
//
// 
//
// 提示： 
//
// 
// 0 <= capacity <= 10⁴ 
// 0 <= key <= 10⁵ 
// 0 <= value <= 10⁹ 
// 最多调用 2 * 10⁵ 次 get 和 put 方法 
// 
//
// Related Topics 设计 哈希表 链表 双向链表 👍 638 👎 0

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TreeSet;

class LfuCache {
    public static void main(String[] args) {
        //创建该题目的对象方便调用
        LFUCache solution = new LfuCache().new LFUCache(1);
        Map<Integer,Integer> map = new HashMap<>();
        System.out.println(map);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class LFUCache {

    class Node implements Comparable<Node>{

        int cnt;
        int time;
        int key;
        int value;

        Node(int cnt,int time,int key,int value){
            this.cnt = cnt;
            this.time = time;
            this.key = key;
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj){
                return true;
            }
            if(obj instanceof Node){
                Node rhs = (Node) obj;
                return this.cnt == rhs.cnt && this.time == rhs.time;
            }
            return false;
        }

        @Override
        public int compareTo(Node o) {
            return cnt == o.cnt ? time - o.time : cnt-o.cnt;
        }

        @Override
        public int hashCode(){
            return cnt * 1000000007 + time;
        }
    }


    private int capacity;
    private int time;
    Map<Integer,Node> key_table;
    TreeSet<Node> set;
    public LFUCache(int capacity) {
        this.capacity = capacity;
        this.time = 0;
        key_table = new HashMap<>();
        set = new TreeSet<>();
    }
    
    public int get(int key) {

        if(capacity == 0){
            return -1;
        }

        if(!key_table.containsKey(key)){
            return -1;
        }

        //从哈希表中得到旧的缓存
        Node cache = key_table.get(key);
        set.remove(cache);

        cache.cnt += 1;
        cache.time = ++time;
        set.add(cache);

        key_table.put(key,cache);
        return cache.value;
    }
    
    public void put(int key, int value) {
        if(capacity == 0){
            return;
        }
        if(!key_table.containsKey(key)){
            if(key_table.size() == capacity){
                key_table.remove(set.first().key);
                set.remove(set.first());
            }

            Node cache = new Node(1,++time,key,value);

            //将新缓存放入哈希表和平衡二叉树中
            key_table.put(key,cache);

            set.add(cache);
        } else {
           Node cache = key_table.get(key);
           set.remove(cache);
           cache.cnt += 1;
           cache.time = ++time;
           cache.value = value;
           set.add(cache);
           key_table.put(key,cache);
        }
    }
}

/**
 * Your LFUCache object will be instantiated and called as such:
 * LFUCache obj = new LFUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
//leetcode submit region end(Prohibit modification and deletion)

}